﻿using Manhua3.Common;
using Manhua3.DataModel;
using SharpCompress.Reader;
using SharpCompress.Reader.Rar;
using SharpCompress.Reader.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using Manhua3.Helper;

namespace Manhua3.Provider
{
    public sealed class LocalFileProvider : IDisposable
    {
        private IStorageFile _zipFile;
        private object _lock = new object();
        private Stream stream;

        private string localName;
        public string LocalName
        {
            get { return localName; }
        }

        private IStorageFolder _folder;

        public LocalFileProvider(IStorageFile file)
        {
            this._zipFile = file;
            this.localName = Path.GetFileNameWithoutExtension(file.Name);
        }

        public LocalFileProvider(IStorageFolder folder)
        {
            this.localName = folder.Name;
            this._folder = folder;
        }

        public async Task<List<ComicPage>> GetPagesAsync()
        {
            if (this._zipFile != null)
                return await this.ReadFileFromZip();

            if (this._folder != null)
                return await this.ReadFileFromFolder();

            return new List<ComicPage>();
        }

        public async Task OpenAsync()
        {
            if (this.stream == null)
                this.stream = await this._zipFile.OpenStreamForReadAsync();

            this.stream.Seek(0, SeekOrigin.Begin);
        }

        private async Task<List<ComicPage>> ReadFileFromZip()
        {
            List<ComicPage> results = new List<ComicPage>();

            await this.OpenAsync();
            IReader reader = this.OpenReader();

            byte[] buffer = new byte[40960];
            int i = 0;
            while (reader.MoveToNextEntry())
            {
                if (reader.Entry.IsDirectory)
                    continue;

                switch (Path.GetExtension(reader.Entry.FilePath).ToLower())
                {
                    case ".jpg":
                    case ".png":
                    case ".bmp":
                        var page = new ComicPage()
                        {
                            SourceUrl = reader.Entry.FilePath,
                            Page = ++i,
                            IsPart = false,
                        };

                        var ms = new InMemoryRandomAccessStream();
                        var ws = ms.AsStreamForWrite();
                        using (var tmp = new MemoryStream())
                        {
                            reader.WriteEntryTo(tmp);
                            tmp.Seek(0, SeekOrigin.Begin);

                            int len;
                            while ((len = tmp.Read(buffer, 0, buffer.Length)) != 0)
                            {
                                ws.Write(buffer, 0, buffer.Length);
                            }

                            ws.Flush();
                        }

                        page.SourceStream = ms;
                        if (await page.SourceStream.CheckImageCanHalf())
                        {
                            page.IsPart = true;
                        }

                        results.Add(page);
                        break;
                }
            }

            reader.Dispose();
            this.stream.Dispose();
            this.stream = null;

            SortList(results);
            return results;
        }

        private async Task<List<ComicPage>> ReadFileFromFolder()
        {
            List<ComicPage> results = new List<ComicPage>();

            var query = await this._folder.GetFilesAsync();
            int i = 0;
            foreach (var file in query)
            {
                switch (Path.GetExtension(file.Name).ToLower())
                {
                    case ".jpg":
                    case ".bmp":
                    case ".png":

                        var page = new ComicPage()
                        {
                            SourceStream = await file.OpenAsync(FileAccessMode.Read),
                            Page = ++i,
                            IsPart = false
                        };

                        if (await page.SourceStream.CheckImageCanHalf())
                        {
                            page.IsPart = true;
                        }

                        results.Add(page);
                        break;
                }
            }

            return results;
        }
        
        private void SortList(List<ComicPage> pages)
        {
            for (int i = 0; i < pages.Count; i++)
            {
                for (int j = pages.Count - 1; j > i; j--)
                {
                    if (string.CompareOrdinal(pages[j].SourceUrl, pages[j - 1].SourceUrl) < 0)
                    {
                        var tmp = pages[j];
                        pages[j] = pages[j - 1];
                        pages[j - 1] = tmp;
                    }
                }

                pages[i].Page = i + 1;
            }
        }

        public static async Task CleanTempAsync()
        {
            try
            {
                var folder = await ApplicationData.Current.TemporaryFolder.GetFolderAsync("zip");
                await folder.DeleteAsync(StorageDeleteOption.PermanentDelete);
            }
            catch
            { 
            }
        }

        private IReader OpenReader()
        {
            IReader reader;

            SharpCompress.Common.ArchiveEncoding.Default = Encoding.GetEncoding("gb2312");

            switch (this._zipFile.FileType.ToLower())
            {
                case ".zip":
                    reader = ZipReader.Open(stream);
                    break;

                case ".rar":
                    reader = RarReader.Open(stream);
                    break;

                default:
                    throw new ArgumentException();
            }

            return reader;
        }
                        
        public void Dispose()
        {
            if (this.stream != null)
                this.stream.Dispose();
        }
    }
}
